package com.siemens.industrial.web.mvc.controller.admin;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.siemens.industrial.model.JobType;
import com.siemens.industrial.model.admin.Device;
import com.siemens.industrial.model.admin.GatewayState;
import com.siemens.industrial.model.partner.DeviceState;
import com.siemens.industrial.model.admin.GatewayInfo;
import com.siemens.industrial.utils.*;
import com.siemens.industrial.web.mvc.dao.admin.DeviceDao;
import com.siemens.industrial.web.mvc.service.admin.DeviceService;
import com.siemens.industrial.web.mvc.service.admin.SystemConfigService;
import com.siemens.industrial.web.mvc.service.partner.PartnerDeviceStatePeriodService;
import com.siemens.industrial.web.mvc.service.partner.PartnerDeviceStateService;
import org.apache.http.HttpEntity;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by qupen on 2016/9/27.
 * <p>
 * Note: APIs in this controller should not be intercepted, all gateways can access them.
 */
@Controller
public class PublicController {
    @Resource
    DeviceService deviceService;

    @Resource
    DeviceDao deviceDao;

    @Resource
    SystemConfigService systemConfigService;

    @Resource
    PartnerDeviceStateService partnerDeviceStateService;

    @Resource
    PartnerDeviceStatePeriodService partnerDeviceStatePeriodService;

    private static final ObjectMapper objectMapper = new ObjectMapper();

    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * receive device status from gateway, log it to file, then update it in device state table.(or just add? if do like this, too much memory will be paste, need more investigate)
     * note: the api not test since less time.
     *
     * @param gatewayStatus
     * @return
     */
    @RequestMapping(value = "/notify", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> getStatus(@RequestBody Map<String, Object> gatewayStatus) {
        try {
            Device device = deviceService.getDeviceByUniqueId(gatewayStatus.get("MTId").toString());

            if (device == null) {
                return RestResponse.create(GlobalResponseCode.ERROR_DEVICE_NOT_EXISTED).build();
            }
            String temp = gatewayStatus.get("time").toString();
            long time = Long.parseLong(temp);

            if (temp.length() == 10) {
                time = time * 1000;
            }
            if (!gatewayStatus.containsKey("statusType")) {
                return RestResponse.create(GlobalResponseCode.ERROR_DEVICE_NOT_EXISTED).build();
            }

            int statusState = Integer.parseInt(gatewayStatus.get("statusType").toString());

            if (!partnerDeviceStateService.isExist(device.getTenantId(), device.getDeviceId(), time, statusState)) {
                // if new status, then update it in state table
                DeviceState deviceState = new DeviceState();
                deviceState.setDeviceId(device.getDeviceId());
                deviceState.setState(Integer.parseInt(gatewayStatus.get("value").toString()));
                deviceState.setStatusType(Integer.parseInt(gatewayStatus.get("statusType").toString()));
                deviceState.setTime(new Date(time));
                partnerDeviceStateService.saveDeviceState(device.getTenantId(), deviceState);

                if (time < getZero()) {
                    String info = "{\"deviceId\":\"" + device.getDeviceId() + "\",\"time\":\"" + time + "\",\"statusType\":\"" + statusState + "\"}";
                    SocketClient socketClient = new SocketClient();
                    socketClient.sendMessage(JobType.Device_Status_Dispose.toString(), info);
                }
            }
            return RestResponse.create(GlobalResponseCode.SUCCESS).build();
        } catch (SiemensException se) {
            return RestResponse.create(se.getResponseCode()).build();
        }
    }

    @RequestMapping(value = "/heartbeat", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> heartbeat(@RequestBody Map<String, Object> entity) {
        GatewayState gatewayState = new GatewayState();
        if (entity.containsKey("version")) {
            gatewayState.setVersion(Integer.parseInt(String.valueOf(entity.get("version"))));
        }
        if (entity.containsKey("clientId")) {
            gatewayState.setClientId(String.valueOf(entity.get("clientId")));
        }

        String temp = String.valueOf(entity.get("time"));
        long time = Long.parseLong(temp);

        if (temp.length() == 10) {
            time = time * 1000;
        }
        if (entity.containsKey("time")) {
            gatewayState.setTime(new Date(time));
        }
        if (entity.containsKey("power")) {
            gatewayState.setPower(Integer.parseInt(String.valueOf(entity.get("power"))));
        }

        deviceDao.saveGateweyState(gatewayState);
        return RestResponse.create(GlobalResponseCode.SUCCESS).build();
    }

    @RequestMapping(value = "/rawdata/ad", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> saveRewDataAd(@RequestBody String rawdata) {
        if (rawdata == null || rawdata.equals("")) {
            return RestResponse.create(GlobalResponseCode.ERROR_AD_DATA_NOT_EXISTED).build();
        }
        String ad = ConfigurationUtil.INSTANCE.getAddPath();
        boolean success = SiemensFileWriter.writeToFile(ad, simpleDateFormat.format(new Date()), rawdata, true);

        if (!success) {
            return RestResponse.create(GlobalResponseCode.ERROR_FILE_IN_USE).build();
        }

        return RestResponse.create(GlobalResponseCode.SUCCESS).build();
    }

    @RequestMapping(value = "/rawdata/acc", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> saveRewDataAcc(@RequestBody String rawdata) {
        if (rawdata == null || rawdata.equals("")) {
            return RestResponse.create(GlobalResponseCode.ERROR_ACC_DATA_NOT_EXISTED).build();
        }
        String ac = ConfigurationUtil.INSTANCE.getAcPath();
        boolean success = SiemensFileWriter.writeToFile(ac, simpleDateFormat.format(new Date()), rawdata, true);

        if (!success) {
            return RestResponse.create(GlobalResponseCode.ERROR_FILE_IN_USE).build();
        }
        return RestResponse.create(GlobalResponseCode.SUCCESS).build();
    }

    @RequestMapping(value = "/register", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> register(@RequestBody Map<String, String> data, HttpServletRequest request) {
        GatewayInfo gatewayInfo = new GatewayInfo();
        if (data.containsKey("clientId")) {
            gatewayInfo.setClientId(data.get("clientId"));
        }
        if (data.containsKey("version")) {
            gatewayInfo.setVersion(data.get("version"));
        }
        if (data.containsKey("hardware")) {
            gatewayInfo.setHardware(data.get("hardware"));
        }
        if (data.containsKey("software")) {
            gatewayInfo.setSoftware(data.get("software"));
        }

        GatewayInfo gatewayInfoDB = deviceDao.getOneGateweyInfoByClientid(data.get("clientId"));

        if (gatewayInfoDB != null) {
            return RestResponse.create(GlobalResponseCode.SUCCESS).build();
        }

        String url = request.getRemoteAddr() + "/rawdata/push";
        String givenUrl = request.getHeaders("host").nextElement();
        HashMap body = new HashMap() {{
            put("url", givenUrl);
            put("interval", (new Date().getTime()) / 1000);
        }};
        String response = "";
        try {
            URI uri = new URIBuilder(url).build();
            HttpEntity entity = Request.Post(uri).bodyString(objectMapper.writeValueAsString(body), ContentType.APPLICATION_JSON).execute().returnResponse().getEntity();
            response = EntityUtils.toString(entity, StandardCharsets.UTF_8);
        } catch (URISyntaxException | IOException e) {
            e.printStackTrace();
        }
        if (!response.equals("OK")) {
            System.out.println("send rawdata push failure");
        }
        deviceDao.saveGateweyInfo(gatewayInfo);
        return RestResponse.create(GlobalResponseCode.SUCCESS).build();
    }

    /**
     * 当天0点的时间戳
     *
     * @return
     */
    private long getZero() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, 0);

        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTimeInMillis();
    }

    public void getMIId() {
        List<Device> devices = deviceDao.getDeviceListOrderByMTId();

    }
}
